import { _decorator } from "cc";
import Singleton from "../Base/Singleton";
import { ApiMsgEnum, binaryDecode, binaryEncode, IModel, strdecode, strencode } from "../Common";

interface IItem {
  cb: Function;
  ctx: unknown;
}

interface ICallApiRes<T> {
  success: boolean,
  error?: Error,
  res?: T
}


export class NetWorkManager extends Singleton {
  static get Instance() {
    return super.GetInstance<NetWorkManager>();
  }
  isConnect = false;
  port = 9876
  ws: WebSocket
  private map: Map<ApiMsgEnum, Array<IItem>> = new Map();

  connect() {
    return new Promise((resolve, reject) => {
      if (this.isConnect) {
        resolve(true)
        return;
      }
      this.ws = new WebSocket(`ws://localhost:${this.port}`)
      this.ws.binaryType = "arraybuffer";
      this.ws.onopen = () => {
        this.isConnect = true;
        resolve(true)
      }
      this.ws.onclose = () => {
        this.isConnect = false;
        reject(false)
      }
      this.ws.onerror = () => {
        console.log("连接错误");
        this.isConnect = false;
        reject(false)
      }
      this.ws.onmessage = (e) => {
        try {
          const json = binaryDecode(e.data);
          const { name, data } = json;

          if (this.map.has(name)) {
            this.map.get(name).forEach(({ cb, ctx }) => {
              cb.call(ctx, data);
            });
          }
        } catch (error) {
          console.error(error);
        }
      }
    })

  }

  callApi<T extends keyof IModel["api"]>(name: T, data: IModel["api"][T]["req"]): Promise<ICallApiRes<IModel["api"][T]["res"]>> {
    return new Promise((resolve) => {
      try {
        const timer = setTimeout(() => {
          resolve({ success: false, error: new Error("超时") });
          this.unlistenMsg(name as any, cb, null);
        }, 5000);
        const cb = (data) => {
          resolve(data);
          clearTimeout(timer);
          this.unlistenMsg(name as any, cb, null);
        }
        this.listenMsg(name as any, cb, null)
        this.sendMsg(name as any, data);
      } catch (e) {
        console.error(e);
      }
    })
  }

  async sendMsg<T extends keyof IModel["msg"]>(name: T, data: IModel["msg"][T]) {
    const msg = { name, data };
    const da = binaryEncode(name, data);
    this.ws.send(da.buffer);
  }

  listenMsg<T extends keyof IModel["msg"]>(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) {
    if (this.map.has(name)) {
      this.map.get(name).push({ cb, ctx });
    } else {
      this.map.set(name, [{ cb, ctx }]);
    }
  }

  unlistenMsg<T extends keyof IModel["msg"]>(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) {
    if (this.map.has(name)) {
      this.map.set(name, this.map.get(name).filter((item) => item.cb !== cb));
    }
  }

}
